/*
 * @Author: wuqingchun
 * @Date: 2024-04-25 10:41:51
 * @Description: 测试原子变量的性能
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-04-25 15:05:59
 */
#ifndef __PERF_ATOMIC_H__
#define __PERF_ATOMIC_H__

#include <iostream>
#include <atomic>
#include <vector>
#include <thread>

#include "perf_time.h"
#include "perf_affinity.h"

class PerfAtomic
{
public:
    uint64_t GetNextCounter(std::memory_order enMemOrder)
    {
        return m_ui64AtomCouter.fetch_add(1, enMemOrder);
    }

    void Start(uint32_t uiThreadCount, uint32_t uiRunTimes, std::memory_order enMemOrder)
    {
        std::pair<uint64_t, uint64_t> objTmPair {0, 0};

        for (uint32_t uiTmp = 0; uiTmp < uiThreadCount; uiTmp++)
        {
            m_vecCostTime.push_back({0, 0});
        }
        
        for (uint32_t uiTmp = 0; uiTmp < uiThreadCount; uiTmp++)
        {
            // m_vecPair.push_back(std::pair<std::thread, uint64_t>(
            //     std::thread([this, enMemOrder, uiRunTimes, uiTmp](){
            //         uint64_t ui64Start, ui64End;

            //         for (uint32_t uiTimes = 0; uiTimes < uiRunTimes; uiTimes++)
            //         {
            //             ui64Start = PerfTime::GetRDTSC();
            //             this->GetNextCounter(enMemOrder);
            //             ui64End = PerfTime::GetRDTSC();

            //             this->m_vecPair[uiTmp].second += (ui64End - ui64Start);
            //         }

            //     }), 0));

            // m_vecCostTime.push_back(0);
            m_vecThreads.push_back(std::thread([this, enMemOrder, uiRunTimes, uiTmp](){
                    uint64_t ui64Start, ui64End;
                    uint64_t ui64StartTs, ui64EndTs;

                    PerfAffinity::SetThreadAffinity(uiTmp + 1);
                    
                    for (uint32_t uiTimes = 0; uiTimes < uiRunTimes; uiTimes++)
                    {
                        // ui64StartTs = PerfTime::GetChronoTime();
                        ui64StartTs = PerfTime::GetClockTime();
                        ui64Start = PerfTime::GetRDTSC();
                        this->GetNextCounter(enMemOrder);
                        ui64End = PerfTime::GetRDTSC();
                        // ui64EndTs = PerfTime::GetClockTime();
                        ui64EndTs = PerfTime::GetChronoTime();

                        this->m_vecCostTime[uiTmp].first += (ui64EndTs - ui64StartTs);
                        this->m_vecCostTime[uiTmp].second += (ui64End - ui64Start);
                    }
                }));
        }

        for (auto& refThread : m_vecThreads)
        {
            if (refThread.joinable())
            {
                refThread.join();
            }
        }

        m_vecThreads.clear();

        for (auto item : m_vecCostTime)
        {
            objTmPair.first += item.first;
            objTmPair.second += item.second;
        }

        m_vecCostTime.clear();

        std::cout << "     Order: " << enMemOrder << std::endl 
                  << "     Times: " << uiRunTimes * uiThreadCount << std::endl 
                  << " TotalCost: " << objTmPair.first << ", " << objTmPair.second  << std::endl
                  << "   Elapsed: " << objTmPair.first/(uiRunTimes * uiThreadCount) <<  ", " << objTmPair.second/(uiRunTimes * uiThreadCount) << std::endl << std::endl;
    }

private:
    std::atomic<uint64_t> m_ui64AtomCouter {0}; 
    std::vector<std::pair<std::thread, uint64_t>> m_vecPair;
    std::vector<std::thread> m_vecThreads;
    std::vector<std::pair<uint64_t, uint64_t>> m_vecCostTime;
};

#endif //__PERF_ATOMIC_H__